package sets;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import norms.SNorm;
import norms.TNorm;

import universe.Discrete;
import universe.IUniverseOfDiscourse;
import utils.Element;
import utils.Interval;
import memberships.MembershipFunction;


public class FuzzySetType1 implements IFuzzySet {

	private MembershipFunction membershipFunction;
	private IUniverseOfDiscourse universe;
	private List<Element> elements;
	private String column;
	private Type type = Type.TYPE1;
	
	public FuzzySetType1() {
		elements = new ArrayList<>();
	}

	@Override
	public Boolean isEmpty() {
		if (hgt().toDouble() == 0) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public Boolean isConcave() {
		// TODO Auto-generated method stub
		throw new UnsupportedOperationException("Method isConcave has not been implemented");
		//return null;
	}

	@Override
	public Boolean isConvex() {
		// TODO Auto-generated method stub
		throw new UnsupportedOperationException("Method isConvex has not been implemented");
		//return null;
	}

	@Override
	public Boolean isNormal() {
		if (hgt().toDouble() == 1) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public ClassicalSet supp() {
		ClassicalSet support = new ClassicalSet();
		Discrete universe = new Discrete();
		
		for (Element element : elements) {
			if (element.v > 0) {
				universe.addElement(element.e);
			}
		}
		
		support.setUniverseOfDiscourse(universe);		
		return support;
	}

	@Override
	public Interval sup() {
		double supremum = 0.0;
		
		for (Element element : elements) {
			if (element.v > supremum) {
				supremum = element.v;
			}
		}
		
		return new Interval(supremum, supremum);
	}

	@Override
	public Interval card() {
		double cardinality = 0.0;
		
		for (Element element : elements) {
			cardinality += element.v;
		}
		
		return new Interval(cardinality, cardinality);
	}

	@Override
	public Interval in() {
		double degree = 0.0;
		
		for (Element element : elements) {
			if (element.v > 0) {
				degree++;
			}
		}
		
		degree /= elements.size();		
		return new Interval(degree, degree);
	}

	@Override
	public Interval c() {
		double numerator = 0;
		double denominator = 0;
		
		for (Element element : elements) {
			numerator += element.e * element.v;
			denominator += element.v;
		}
		
		double centroid = numerator / denominator;
		return new Interval(centroid, centroid);
	}
	
	@Override
	public Interval hgt() {
		double height = 0.0;
		
		for (Element element : elements) {
			if (element.v > height) {
				height = element.v;
			}
		}
		
		return new Interval(height, height);
	}

	@Override
	public ClassicalSet core() {
		ClassicalSet coreSet = new ClassicalSet();
		Discrete universe = new Discrete();
		
		for (Element element : elements) {
			if (element.v == 1) {
				universe.addElement(element.e);
			}
		}
		
		coreSet.setUniverseOfDiscourse(universe);
		return coreSet;
	}

	@Override
	public ClassicalSet alphaCut(double alpha) {
		ClassicalSet alphacut = new ClassicalSet();
		Discrete universe = new Discrete();
		
		for (Element element : elements) {
			if (element.v >= alpha) {
				universe.addElement(element.e);
			}
		}
		
		alphacut.setUniverseOfDiscourse(universe);
		return alphacut;
	}

	@Override
	public IFuzzySet union(IFuzzySet set) {
		IFuzzySet unionSet = new FuzzySetType1();
		List<Element> unionElements = new ArrayList<>();
		SNorm norm = new SNorm();
		
		Iterator<Element> it;
		List<Double> listOfElements = new ArrayList<>();
		
		// Copy elements from both sets to HashMaps
		Map<Double, Double> setA = new HashMap<>();
		Map<Double, Double> setB = new HashMap<>();
		
		it = elements.iterator();
		while (it.hasNext()) {
			Element element = it.next();
			setA.put(element.e, element.v);
			listOfElements.add(element.e);			// <-- collect list of elements
		}
		
		it = set.getElements().iterator();
		while (it.hasNext()) {
			Element element = it.next();
			setB.put(element.e, element.v);
			listOfElements.add(element.e);			// <-- collect list of elements
		}
		
		for (Double element : listOfElements) {
			double vA = 0.0;
			double vB = 0.0;
			
			if (setA.containsKey(element)) {
				vA = setA.get(element);
			} else {
				vA = 0.0;
			}
			
			if (setB.containsKey(element)) {
				vB = setB.get(element);
			} else {
				vB = 0.0;
			}
			
			double max = norm.max(vA, vB);
			
			if (max == vA) {
				unionElements.add(new Element(element, vA));
			} else {
				unionElements.add(new Element(element, vB));
			}
		}
		
		unionSet.setElements(unionElements);
		return unionSet;
	}

	@Override
	public IFuzzySet intersection(IFuzzySet set) {
		IFuzzySet intersectionSet = new FuzzySetType1();
		List<Element> intersectionElements = new ArrayList<>();
		TNorm norm = new TNorm();
		
		Iterator<Element> it;
		List<Double> listOfElements = new ArrayList<>();
		
		// Copy elements from both sets to HashMaps
		Map<Double, Double> setA = new HashMap<>();
		Map<Double, Double> setB = new HashMap<>();
		
		it = elements.iterator();
		while (it.hasNext()) {
			Element element = it.next();
			setA.put(element.e, element.v);
			listOfElements.add(element.e);			// <-- collect list of elements
		}
		
		it = set.getElements().iterator();
		while (it.hasNext()) {
			Element element = it.next();
			setB.put(element.e, element.v);
			listOfElements.add(element.e);			// <-- collect list of elements
		}
		
		for (Double element : listOfElements) {
			double vA = 0.0;
			double vB = 0.0;
			
			if (setA.containsKey(element)) {
				vA = setA.get(element);
			} else {
				vA = 0.0;
			}
			
			if (setB.containsKey(element)) {
				vB = setB.get(element);
			} else {
				vB = 0.0;
			}
			
			double max = norm.min(vA, vB);
			
			if (max == vA) {
				intersectionElements.add(new Element(element, vA));
			} else {
				intersectionElements.add(new Element(element, vB));
			}
		}
		
		intersectionSet.setElements(intersectionElements);
		return intersectionSet;
	}

	@Override
	public IFuzzySet complement(IFuzzySet set) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setMembershipFunction(MembershipFunction membershipFunction) {
		this.membershipFunction = membershipFunction;
		
		/**
		 * Not in use anymore!
		 */
		//if (universe != null) {
		//	computeValuesOfElements();
		//}
	}

	@Override
	public MembershipFunction getMembershipFunction() {
		return membershipFunction;
	}

	@Override
	public void setUniverseOfDiscorse(IUniverseOfDiscourse universe) {
		this.universe = universe;
		
		/**
		 * Not in use anymore!
		 */
		//if (membershipFunction != null) {
		//	computeValuesOfElements();
		//}
	}

	@Override
	public IUniverseOfDiscourse getUniverseOfDiscorse() {
		return universe;
	}
	
	@Override
	public void setElements(List<Element> elements) {
		this.elements = elements;
	}
	
	@Override
	public void addElement(Double element) {
		Element e = new Element();
		e.e = element;
		e.v = membershipFunction.y(element);
		elements.add(e);
	}
	
	@Override
	public List<Element> getElements() {
		return elements;
	}
	
	/**
	 * Not in use anymore!
	 */
	@Deprecated
	private void computeValuesOfElements() {
		elements.clear();
		Iterator<Double> it = universe.getElements().iterator();
		
		Element element;
		
		while (it.hasNext()) {
			element = new Element();
			element.e = it.next();
			element.v = membershipFunction.y(element.e);
			elements.add(element);
		}
		
		System.out.println("Values for each element has been computed.");
	}
	
	public void showElements() {
		Iterator<Element> it = elements.iterator();
		
		while (it.hasNext()) {
			System.out.println(it.next());
		}
	}

	@Override
	public Map<Double, Double> getElementsAsMap() {
		Map<Double, Double> elementsAsMap = new HashMap<>();
		
		Iterator<Element> it = elements.iterator();
		
		while (it.hasNext()) {
			Element element = it.next();
			elementsAsMap.put(element.e, element.v);
		}
		return elementsAsMap;
	}

	@Override
	public void setColumn(String column) {
		this.column = column;
	}

	@Override
	public String getColumn() {
		return column;
	}

	@Override
	public Type getType() {
		return type;
	}
	
}
